package com.zlyx.easy.database.aspects;

import java.lang.reflect.Method;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import com.zlyx.easy.core.loggers.Logger;
import com.zlyx.easy.core.map.EasyMap;
import com.zlyx.easy.core.map.Maps;
import com.zlyx.easy.core.spring.SpringUtils;
import com.zlyx.easy.core.utils.MethodUtils;
import com.zlyx.easy.core.utils.ObjectUtils;
import com.zlyx.easy.database.annotations.EasySelect;
import com.zlyx.easy.database.interfaces.SqlFilter;
import com.zlyx.easy.database.local.ReturnType;
import com.zlyx.easy.database.supports.SqlFactory;

/**
 * @Auth 赵光
 * @Describle
 * @2019年1月13日 下午7:13:00
 */
public abstract class AbstractSelectAspect {

	private static Map<Method, SqlFactory> SqlFactorys = Maps.newConcurrentHashMap();

	public final static String DEFAULTPARSER = "defaultParser";

	@Pointcut("@annotation(com.zlyx.easy.database.annotations.EasySelect) ||"
			+ "@annotation(org.apache.ibatis.annotations.Select)||"
			+ "@annotation(org.apache.ibatis.annotations.SelectProvider)")
	public void select() {
	}

	@Around("select()")
	public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
		Object res = null;
		Signature signature = pjp.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		Class<?> proxyCls = method.getDeclaringClass();
		try {
			if (!isAspect(method)) {
				return pjp.proceed();
			}
			EasySelect select = method.getAnnotation(EasySelect.class);
			if (ObjectUtils.isNotEmpty(select)) {
				if (ReturnType.getType() == null) {
					ReturnType.setType(method);
				}
				SqlFactory factory = SqlFactorys.get(method);
				if (factory == null) {
					factory = new SqlFactory(select.tableCls());
					if (!factory.results(select.results())) {
						return null;
					}
					SqlFactorys.put(method, factory);
				}
				EasyMap<String, Object> paramsMap = MethodUtils.getParamsMap(method, pjp.getArgs());
				if (select.page()) {
					if (paramsMap.withoutKey(EasySelect.PAGENUM)) {
						Logger.err(proxyCls, "方法参数中不包含pageNum参数!无法完成分页!");
						return null;
					}
					if (paramsMap.withoutKey(EasySelect.PAGSIZE)) {
						Logger.err(proxyCls, "方法参数中不包含pageSize参数!无法完成分页!");
						return null;
					}
				}
				factory.where(select.where(), paramsMap);
				Logger.info(proxyCls, "预编译SQL@" + factory);
				factory = doFilter(factory, method);
				Logger.info(proxyCls, "成熟态SQL@" + factory);
				ReturnType.setType(method);
				String sql = factory.assemble(pjp.getTarget());
				res = doAspect(method, select.page(), sql, paramsMap);
				ReturnType.clear();
			} else {
				res = pjp.proceed();
			}
		} catch (Throwable e) {
			Logger.err(pjp.getTarget().getClass(), e);
		}
		return res;
	}

	public abstract Object doAspect(Method method, boolean page, String sql, EasyMap<String, Object> paramsMap);

	public abstract boolean isAspect(Method method);

	private SqlFactory doFilter(SqlFactory factory, Method method) {
		Map<String, SqlFilter> filters = SpringUtils.getBeansOfType(SqlFilter.class);
		if (ObjectUtils.isNotEmpty(filters) || filters.size() != 0) {
			SqlFactory filterfactory = null;
			for (String filterName : filters.keySet()) {
				filterfactory = filters.get(filterName).doFilter(factory, method);
				if (filterfactory != null) {
					factory = filterfactory;
				}
			}
		} else {
			Logger.debug(method.getDeclaringClass(), "用户未实现自定义SQL编译前过滤器，直接编译!");
		}
		return factory;
	}

}
